package com.atguigu.spzx.common.log.aspect;

import com.alibaba.fastjson.JSON;
import com.atguigu.spzx.common.exception.SpzxException;
import com.atguigu.spzx.common.interceptor.SpzxLoginAuthInterceptor;
import com.atguigu.spzx.common.log.anno.Log;
import com.atguigu.spzx.common.log.service.AsyncSysOperLogService;
import com.atguigu.spzx.util.IpUtil;
import com.atguigu.spzx.model.entity.system.SysOperLog;
import com.atguigu.spzx.model.entity.system.SysUser;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;

@Aspect
@Component
public class SpzxLogAspect {
    @Resource
    AsyncSysOperLogService sysOperLogService;
    //环绕通知
//    @Around(value = "execution(要切面的方法的权限修饰符 全类名.方法名(形参列表))")
    //根据注解切面
    @Around(value = "@annotation(log)") //使用log变量的类型作为切入点，匹配到方法后会将方法上的Log注解对象作为参数传入
    public Object around(ProceedingJoinPoint joinPoint,Log log){
        //记录操作日志: 需要使用model项目中的SysOperLog日志类
        //还需要使用SysOperLogService 保存操作日志 : common-log中不能直接引入spzx-manager的依赖
        //      解决： 在common-log中定义SysOperLogService接口，在spzx-manager中创建实现类注入到容器中
        //            在common-log切面类中可以自动装配SysOperLogService类型的对象
        /*
            依赖关系：
                spzx-manager
                    - common-service
                        -- common-util

                        -- common-log
                            --- spzx-model

         */
        SysOperLog sysOperLog = new SysOperLog();
        // 在接口上会使用Log注解，不同的方法上的Log注解通过它携带对应模块的title
        //1、访问的模块标题
        sysOperLog.setTitle(log.title());
        //获取连接点方法对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        System.out.println(signature.getMethod().getDeclaringClass().toString());
        System.out.println(signature.getMethod().getName());
        //2、访问的方法所在类的全类名+方法名
        Method method = signature.getMethod();
        sysOperLog.setMethod(method.getDeclaringClass().getName()+ "."+signature.getMethod().getName()+"()");
        //3、请求方式
        //获取监听器初始化共享的HttpServletRequest对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        sysOperLog.setRequestMethod(request.getMethod());
        //URI:资源路径  URL：服务器资源访问路径
        sysOperLog.setOperUrl(request.getRequestURI());
        sysOperLog.setOperIp(IpUtil.getIpAddress(request));
        //4、通过注解设置携带
        sysOperLog.setOperatorType(log.operatorType());
        sysOperLog.setBusinessType(log.businessType());

        SysUser sysUser = SpzxLoginAuthInterceptor.THREAD_LOCAL.get();
        sysOperLog.setOperName(sysUser.getName());
        //是否需要保存请求参数：通过注解携带
        if(log.isSaveRequestParams()){
            Object[] pointArgs = joinPoint.getArgs();
            sysOperLog.setOperParam(JSON.toJSONString(pointArgs));
        }
        try {
            //初始化日志数据
            Object[] args = joinPoint.getArgs();
            //执行目标方法得到结果
            Object result = joinPoint.proceed(args);
            if(log.isSaveResponseData()){
                //返回目标方法的结果
                //是否需要保存响应结果:通过注解携带
                sysOperLog.setJsonResult(JSON.toJSONString(result));
            }
            sysOperLog.setStatus(0);
            sysOperLog.setErrorMsg(null);
            return result;
        } catch (Throwable e) {
            sysOperLog.setStatus(1);
            sysOperLog.setErrorMsg(e.getMessage());
            throw new SpzxException(ResultCodeEnum.ERROR , null);
        }finally {
            //保存日志
            sysOperLogService.saveSysOperLog(sysOperLog);
        }
    }
}
